package org.example.mall.admin.configuration.auth.handler;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.lang.UUID;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.example.mall.admin.configuration.auth.CustomUser;
import org.example.mall.admin.constant.auth.AuthTypeConst;
import org.example.mall.admin.constant.auth.enums.ClientEnum;
import org.example.mall.admin.model.entity.manage.Account;
import org.example.mall.admin.model.entity.manage.AccountAuth;
import org.example.mall.admin.service.manage.AccountService;
import org.example.mall.admin.util.BusOp;
import org.example.mall.admin.util.SecurityUtil;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

/**
 * @author hzq
 * @date 2021/12/16 14:45
 */
@Slf4j
@Component
@AllArgsConstructor
public class WxMiniAppAuthHandler extends AbstractAuthHandler {

    private final AccountService accountService;

    /**
     * 小程序登录
     * wx.login()
     *
     * @param principal 小程序登录时 获取到到的微信 code
     */
    @Override
    public UserDetails handler(String principal, String credentials, Map<String, String> parameters) {

        AccountAuth auth;

        // todo 自己去配置 小程序配置
        WxMaService wxMaService = new WxMaServiceImpl();

        // todo 租户添加小程序信息 可以通过appId 得到租户信息
        String appId = parameters.get("appId");

        try {
            // 调用微信服务 获取用户信息
            WxMaJscode2SessionResult result = wxMaService
                    .switchoverTo(appId) // 切换到对应的小程序配置
                    .getUserService() // 获取用户服务
                    .getSessionInfo(principal); // 获取用户信息

            // 通过openid 查询本系统是否存在该用户
            auth = getAuth(result.getOpenid());

            if (auth == null) {
                // 创建客户
                auth = createAccountAuth(result);
            }

        } catch (WxErrorException e) {
            throw new BadCredentialsException(e.getLocalizedMessage());
        }

        // 校验用户信息
        checkAuth(auth);

        List<GrantedAuthority> userMenu = accountAuthorities(auth);

        return new CustomUser(auth, userMenu);
    }

    @Override
    public String getHandlerType() {
        return AuthTypeConst.WX_MINI_APP;
    }

    /**
     * 创建用户信息
     *
     * @param result 微信小程序信息
     * @return 认证信息
     */
    AccountAuth createAccountAuth(WxMaJscode2SessionResult result) {

        // 获取客户端id
        // 根据客户端判断是否需要创建用户信息
        String clientId = SecurityUtil.getClientId();

        if (ClientEnum.mobileSide.getClientId().equals(clientId)) {

            // 客户端直接创建客户
            Account account = new Account();
            account.setUnionId(UUID.fastUUID().toString());
            // 1、appId与租户进行绑定 可以通过appId得到租户id
            // 2、前端也可以通过appId查询后端免密接口先得到租户id 然后把租户id加在请求头中
            // 我们使用第二种方案
            BusOp.mustSucceed(accountService.save(account));

            AccountAuth auth = new AccountAuth();
            auth.setAccountId(account.getAccountId());
            auth.setUnionId(account.getUnionId());
            auth.setAuthType(AuthTypeConst.WX_MINI_APP);
            auth.setPrincipal(result.getOpenid());
            BusOp.mustSucceed(getAccountAuthService().save(auth));

            return auth;
        }

        return null;
    }
}
